﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;

using OAuth.Net.Common;
using OAuth.Net.Components;
using OAuth.Net.ServiceProvider;

namespace pyTivoMetadataManager
{
    public class NetflixAPI
    {
        public Uri ApiBaseUri = new Uri("http://api.netflix.com/");
        private WebClient client = new WebClient();

        public Uri BuildRequestUri(Uri requestedResource)
        {
            return (BuildRequestUri(requestedResource, null));
        }

        public Uri BuildRequestUri(Uri requestedResource, Dictionary<string, string> args)
        {
            int timestamp = UnixTime.ToUnixTime(DateTime.Now);

            OAuthParameters parameters = new OAuthParameters();
            parameters.ConsumerKey = KeysSingleton.Instance.NetflixApiKey;
            parameters.Nonce = new GuidNonceProvider().GenerateNonce(timestamp);
            parameters.SignatureMethod = "HMAC-SHA1";
            parameters.Timestamp = timestamp.ToString(CultureInfo.InvariantCulture);
            parameters.Version = "1.0";
            if (args != null)
            {
                foreach (string key in args.Keys)
                {
                    parameters.AdditionalParameters.Add(key, args[key]);
                }
            }

            parameters.Signature = ServiceProviderContext.GetSigningProvider("HMAC-SHA1").ComputeSignature(
                SignatureBase.Create("GET", requestedResource, parameters),
                KeysSingleton.Instance.NetflixSharedSecret,
                null);

            UriBuilder builder = new UriBuilder(requestedResource)
            {
                Query = parameters.ToQueryStringFormat()
            };

            return builder.Uri;
        }

        public Dictionary<string, NetflixTitleInfo> SearchTitles(string title)
        {
            Uri uriTitleSearch = new Uri(ApiBaseUri, "/catalog/titles");
            Dictionary<string, string> argsTitleSearch = new Dictionary<string, string> { { "term", title } };

            Uri uriRequestOAuth = BuildRequestUri(uriTitleSearch, argsTitleSearch);

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(client.DownloadString(uriRequestOAuth));

            XmlNodeList tNodes = doc.SelectNodes("//catalog_title");
            Dictionary<string, NetflixTitleInfo> titles = new Dictionary<string, NetflixTitleInfo>();
            foreach (XmlNode tNode in tNodes)
            {
                NetflixTitleInfo nti = new NetflixTitleInfo();
                string id = tNode.SelectSingleNode("id").InnerText;
                nti.Id = id.Substring(id.LastIndexOf("/")+1);
                XmlNode node = tNode.SelectSingleNode("title");
                nti.Title = node.Attributes["regular"].Value;
                nti.ShortTitle = node.Attributes["short"].Value;
                node = tNode.SelectSingleNode("box_art");
                nti.SmallBoxArt = new Uri(node.Attributes["small"].Value);
                nti.MediumBoxArt = new Uri(node.Attributes["medium"].Value);
                nti.LargeBoxArt = new Uri(node.Attributes["large"].Value);
                nti.SynopsisUri = new Uri(tNode.SelectSingleNode("link[@rel='http://schemas.netflix.com/catalog/titles/synopsis']").Attributes["href"].Value);
                nti.ReleaseYear = int.Parse(tNode.SelectSingleNode("release_year").InnerText);
                node = tNode.SelectSingleNode("category[@scheme='http://api.netflix.com/categories/mpaa_ratings']");
                if (node != null) nti.MpaaRating = node.Attributes["term"].Value;
                node = tNode.SelectSingleNode("category[@scheme='http://api.netflix.com/categories/tv_ratings']");
                if (node != null) nti.TvRating = node.Attributes["term"].Value;
                XmlNodeList gNodes = tNode.SelectNodes("category[@scheme='http://api.netflix.com/categories/genres']");
                foreach (XmlNode gNode in gNodes)
                {
                    nti.Genres.Add(gNode.Attributes["term"].Value);
                }
                node = tNode.SelectSingleNode("runtime");
                if (node != null) nti.Runtime = int.Parse(node.InnerText);
                node = tNode.SelectSingleNode("link[@rel='http://schemas.netflix.com/catalog/people.cast']");
                if (node != null) nti.CastUri = new Uri(node.Attributes["href"].Value);
                node = tNode.SelectSingleNode("link[@rel='http://schemas.netflix.com/catalog/people.directors']");
                if (node != null) nti.DirectorsUri = new Uri(node.Attributes["href"].Value);
                nti.Rating = double.Parse(tNode.SelectSingleNode("average_rating").InnerText);
                nti.MovieUri = new Uri(tNode.SelectSingleNode("link[@rel='alternate']").Attributes["href"].Value);

                titles.Add(nti.Id, nti);
            }

            return(titles);
        }

        public void GetTitleDetails(ref NetflixTitleInfo nti)
        {
            XmlDocument doc;
            XmlNode node;
            XmlNodeList nodes;
            StringWriter sw;

            if (nti.SynopsisUri != null)
            {
                string synopsisXml = client.DownloadString(BuildRequestUri(nti.SynopsisUri));
                if (!string.IsNullOrEmpty(synopsisXml))
                {
                    doc = new XmlDocument();
                    doc.LoadXml(synopsisXml);
                    node = doc.SelectSingleNode("synopsis");
                    sw = new StringWriter();
                    HttpUtility.HtmlDecode(Regex.Replace(node.InnerText, @"<(.|\n)*?>", ""), sw);
                    nti.Synopsis = sw.ToString().Replace('\n', ' ');
                }
            }

            if (nti.CastUri != null)
            {
                string castXml = client.DownloadString(BuildRequestUri(nti.CastUri));
                if (!string.IsNullOrEmpty(castXml))
                {
                    doc = new XmlDocument();
                    doc.LoadXml(castXml);
                    // /people/person/name
                    nodes = doc.SelectNodes("//name");
                    foreach (XmlNode pNode in nodes)
                    {
                        nti.Cast.Add(pNode.InnerText);
                    }
                }
            }
            if (nti.DirectorsUri != null)
            {
                string directorXml = client.DownloadString(BuildRequestUri(nti.DirectorsUri));
                if (!string.IsNullOrEmpty(directorXml))
                {
                    doc = new XmlDocument();
                    doc.LoadXml(directorXml);
                    // /people/person/name
                    nodes = doc.SelectNodes("//name");
                    foreach (XmlNode pNode in nodes)
                    {
                        nti.Directors.Add(pNode.InnerText);
                    }
                }
            }
        }
    }
}
